home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / misc / excalc1.lha / ExCalcV1.1 / Source / ExNumbers.mod < prev    next >
Text File  |  1995-05-08  |  24KB  |  996 lines

  1. (*********************************************************************)
  2. (*                                                                   *)
  3. (* Module ExNumbers Copyright © 1995 by Computer Inspirations        *)
  4. (*                                                                   *)
  5. (* Design : Michael Griebling                                        *)
  6. (* Change : Original                                                 *)
  7. (*                                                                   *)
  8. (*********************************************************************)
  9.  
  10. MODULE ExNumbers;
  11.  
  12. IMPORT io, Cnv := Conversions, S := Strings;
  13.  
  14. CONST
  15.   MaxExp * = 10000;
  16.   MinExp * = -MaxExp;
  17.   HighBoundsManArray * = 52; (* max possible digits--must be multiple of 4. *)
  18.  
  19. TYPE
  20.   ExStatusType * = INTEGER;
  21.  
  22. CONST
  23.   (* ExStatusType values *)
  24.   Okay              *= 0;
  25.   Overflow          *= 1;
  26.   Underflow         *= 2;
  27.   DivideByZero      *= 3;
  28.   TooFewDigits      *= 4;
  29.   TooManyDigits     *= 5;
  30.   IllegalNumber     *= 6;
  31.   UndefinedStorage  *= 7;
  32.   IllegalOperator   *= 8;
  33.   MismatchBraces    *= 9;
  34.  
  35. TYPE
  36.   ExCompareType = INTEGER;
  37.  
  38. CONST
  39.   (* ExCompareType values *)
  40.   ExLess    *= 0;
  41.   ExEqual   *= 1;
  42.   ExGreater *= 2;
  43.  
  44. TYPE
  45.   SignType = SHORTINT;
  46.  
  47. CONST
  48.   (* SignType values *)
  49.   positive *= 0;
  50.   negative *= 1;
  51.  
  52. TYPE
  53.   ManType   * = ARRAY (HighBoundsManArray DIV 4)+2 OF INTEGER;
  54.   ExNumType * = RECORD
  55.                   Man  -: ManType;
  56.                   Sign -: SignType;
  57.                   Zero -: BOOLEAN;
  58.                   Exp  -: INTEGER;
  59.                 END;
  60.  
  61. VAR
  62.   ExStatus * : ExStatusType;
  63.  
  64.   (* Useful constants *)
  65.   e-, ln2-, ln10-, pi-, Ex0-, Ex1-: ExNumType;
  66.  
  67.  
  68. CONST
  69.   MaxLengthNumber = 2 * HighBoundsManArray;
  70.   Dec = 10;
  71.  
  72. VAR
  73.   MaxDigits, MaxQuads : INTEGER;
  74.  
  75.  
  76. PROCEDURE SetMaxDigits *(D : INTEGER);
  77. (* Set maximum digits in extended real numbers -- must be
  78.    a multiple of 4 *)
  79. BEGIN
  80.   IF D < 4 THEN
  81.     MaxDigits := 4;
  82.     ExStatus := TooFewDigits;
  83.   ELSIF D > HighBoundsManArray THEN
  84.     MaxDigits := HighBoundsManArray;
  85.     ExStatus := TooManyDigits;
  86.   ELSE
  87.     MaxDigits := D DIV 4;   (* Force a multiple of 4 *)
  88.     IF D MOD 4 > 0 THEN INC(MaxDigits) END;
  89.     MaxDigits := MaxDigits * 4;
  90.   END;
  91.   MaxQuads := MaxDigits DIV 4;
  92. END SetMaxDigits;
  93.  
  94.  
  95. PROCEDURE ExTimes10 *(VAR A : ExNumType);
  96. (* A := A * 10 -- much faster than ExMult *)
  97. BEGIN
  98.   INC(A.Exp);
  99.   IF A.Exp > MaxExp THEN
  100.     ExStatus := Overflow;
  101.   END;
  102. END ExTimes10;
  103.  
  104.  
  105. PROCEDURE ExDiv10 *(VAR A : ExNumType);
  106. (* A := A / 10 -- much faster than ExDiv *)
  107. BEGIN
  108.   DEC(A.Exp);
  109.   IF A.Exp < MinExp THEN
  110.     ExStatus := Underflow;
  111.   END;
  112. END ExDiv10;
  113.  
  114.  
  115. PROCEDURE IsZero *(A : ExNumType) : BOOLEAN;
  116. VAR
  117.   i : INTEGER;
  118.   Zero : BOOLEAN;
  119. BEGIN
  120.   (* check for zero *)
  121.   i := 0;
  122.   Zero := TRUE;
  123.   WHILE (i <= MaxQuads) AND Zero DO
  124.     IF A.Man[i] # 0 THEN
  125.       Zero := FALSE;
  126.     END;
  127.     INC(i);
  128.   END;
  129.   RETURN Zero;
  130. END IsZero;
  131.  
  132.  
  133. PROCEDURE ExShiftRight(Carry : INTEGER; VAR A : ExNumType);
  134. (* shift all mantissa digits in A to the right one place.
  135.    The most significant digit is replaced with the Carry. *)
  136. VAR
  137.   i : INTEGER;
  138. BEGIN
  139.   (* shift right *)
  140.   FOR i := MaxQuads TO 1 BY -1 DO
  141.     A.Man[i] := A.Man[i] DIV 10 + (A.Man[i-1] MOD 10) * 1000;
  142.   END;
  143.  
  144.   (* put Carry in most significant position *)
  145.   A.Man[0] := A.Man[0] DIV 10 + 1000 * Carry;
  146. END ExShiftRight;
  147.  
  148.  
  149. PROCEDURE ExShiftLeft(VAR A : ExNumType) : INTEGER;
  150. (* shift all mantissa digits in A to the left one place.
  151.    The digit shifted out of the number is returned.
  152.    The least significant digit is replaced with zero. *)
  153. VAR
  154.   i, d : INTEGER;
  155. BEGIN
  156.   (* shift left *)
  157.   d := A.Man[0] DIV 1000;
  158.   FOR i := 0 TO MaxQuads DO
  159.     A.Man[i] := (A.Man[i] MOD 1000) * 10 + A.Man[i+1] DIV 1000;
  160.   END;
  161.  
  162.   (* put zero in least significant position *)
  163.   A.Man[MaxQuads] := (A.Man[MaxQuads] MOD 1000) * 10;
  164.   RETURN d;
  165. END ExShiftLeft;
  166.  
  167.  
  168. PROCEDURE ExChgSign *(VAR A : ExNumType);
  169. (* A := -A *)
  170. BEGIN
  171.   IF A.Sign = positive THEN
  172.     A.Sign := negative;
  173.   ELSE
  174.     A.Sign := positive;
  175.   END;
  176. END ExChgSign;
  177.  
  178.  
  179. PROCEDURE ExAbs *(VAR A : ExNumType);
  180. (* A := ABS(A) *)
  181. BEGIN
  182.   A.Sign := positive;
  183. END ExAbs;
  184.  
  185.  
  186. PROCEDURE ExNorm *(VAR A : ExNumType);
  187. (* Normalise A *)
  188. VAR d : INTEGER;
  189. BEGIN
  190.   (* normalise *)
  191.   IF IsZero(A) THEN
  192.     (* normalize zero *)
  193.     A.Sign := positive;
  194.     A.Exp := 0;
  195.   ELSE
  196.     (* shift mantissa to left until most significant digit is
  197.        non-zero, increment exponent with each shift *)
  198.     WHILE A.Man[0] DIV 1000 = 0 DO
  199.       d := ExShiftLeft(A);
  200.       ExDiv10(A);
  201.     END;
  202.   END;
  203. END ExNorm;
  204.  
  205.  
  206. PROCEDURE GetMaxDigits *() : INTEGER;
  207. (* Get the current number of digits in extended real numbers *)
  208. BEGIN
  209.   RETURN MaxDigits;
  210. END GetMaxDigits;
  211.  
  212.  
  213. PROCEDURE GetExpMant *(x : ExNumType; VAR exp : INTEGER;
  214.                        VAR mant : ExNumType);
  215. (* Returned `mant' number will be between -10.0 and 10.0 *)
  216. BEGIN
  217.   exp := x.Exp;
  218.   mant := x;
  219.   mant.Exp := 0;
  220. END GetExpMant;
  221.  
  222.  
  223. PROCEDURE PutDigit(VAR A : INTEGER; Digit, Index : INTEGER);
  224. BEGIN
  225.   IF Index = 0 THEN
  226.     A := A MOD 1000 + Digit * 1000;
  227.   ELSIF Index = 1 THEN
  228.     A := A DIV 1000 * 1000 + A MOD 100 + Digit * 100;
  229.   ELSIF Index = 2 THEN
  230.     A := A DIV 100 * 100 + A MOD 10 + Digit * 10;
  231.   ELSE
  232.     A := A DIV 10 * 10 + Digit;
  233.   END;
  234. END PutDigit;
  235.  
  236.  
  237. PROCEDURE ExTrunc *(VAR A : ExNumType);
  238. (* Truncate A so no decimal places are kept. *)
  239. VAR
  240.   i : INTEGER;
  241. BEGIN
  242.   IF A.Exp+1 < 0 THEN A := Ex0; RETURN END;
  243.   FOR i := A.Exp+1 TO MaxDigits-1 DO
  244.     (* zero these digits *)
  245.     PutDigit(A.Man[i DIV 4], 0, i MOD 4);
  246.   END;
  247. END ExTrunc;
  248.  
  249.  
  250. PROCEDURE ExFrac *(VAR A : ExNumType);
  251. (* Keep only the fraction portion of A. *)
  252. VAR
  253.   i : INTEGER;
  254. BEGIN
  255.   FOR i := 0 TO A.Exp DO (* zero these digits *)
  256.     PutDigit(A.Man[i DIV 4], 0, i MOD 4);
  257.   END;
  258.   ExNorm(A);             (* normalize the fraction *)
  259. END ExFrac;
  260.  
  261.  
  262. PROCEDURE ExToLongInt *(A : ExNumType) : LONGINT;
  263. (* Convert the extended real number `A' into a INTEGER --
  264.    saturating if necessary. *)
  265. CONST
  266.   MaxDigits = 10;
  267. VAR
  268.   Cnt : INTEGER;
  269.   Int : LONGINT;
  270.   Digit : INTEGER;
  271.   Negative : BOOLEAN;
  272. BEGIN
  273.   Negative := FALSE;
  274.   IF A.Sign = negative THEN
  275.     Negative := TRUE;
  276.     ExAbs(A);
  277.   END;
  278.   IF A.Exp < 0 THEN
  279.     Int := 0;
  280.   ELSIF A.Exp >= MaxDigits THEN
  281.     Int := MAX(LONGINT);
  282.   ELSE
  283.     Int := 0;
  284.     FOR Cnt := 0 TO A.Exp DO
  285.       Digit := ExShiftLeft(A);
  286.       IF Cnt = MaxDigits-1 THEN
  287.         IF Int > MAX(LONGINT) DIV 10 THEN
  288.           RETURN Int;
  289.         END;
  290.         IF (Int = MAX(LONGINT) DIV 10) & (Digit > 6) THEN
  291.           Digit := 6;
  292.         END;
  293.       END;
  294.       Int := Int * 10 + Digit;
  295.     END;
  296.   END;
  297.   IF Negative THEN
  298.     RETURN -Int;
  299.   ELSE
  300.     RETURN Int;
  301.   END;
  302. END ExToLongInt;
  303.  
  304.  
  305. PROCEDURE ExCompare *(A, B : ExNumType) : ExCompareType;
  306. (* Compares the two extended real numbers. *)
  307. VAR
  308.   Done : BOOLEAN;
  309.   i : INTEGER;
  310. BEGIN
  311.   IF A.Sign # B.Sign THEN
  312.     (* A and B have different signs *)
  313.     IF A.Sign = positive THEN
  314.       (* A and B have different signs and A is positive so A>B *)
  315.       RETURN ExGreater;
  316.     ELSE
  317.       (* A and B have different signs and A is negative so A<B *)
  318.       RETURN ExLess;
  319.     END;
  320.   ELSE
  321.     (* A and B have the same sign *)
  322.     IF (A.Exp # B.Exp) & NOT IsZero(B) & NOT IsZero(A) THEN
  323.       IF A.Exp > B.Exp THEN
  324.         (* A exponent > B exponent *)
  325.         IF A.Sign = positive THEN
  326.           RETURN ExGreater;
  327.         ELSE
  328.           RETURN ExLess;
  329.         END;
  330.       ELSE
  331.         (* A exponent <= B exponent *)
  332.         IF A.Sign = positive THEN
  333.           RETURN ExLess;
  334.         ELSE
  335.           RETURN ExGreater;
  336.         END;
  337.       END;
  338.     ELSE
  339.       (* A & B have same sign and A exponent = B exponent *)
  340.       Done := FALSE;
  341.       i := 0;
  342.  
  343.       (* compare each digit until a difference is found or
  344.          we reach the end *)
  345.       WHILE (i <= MaxQuads) AND NOT Done DO
  346.         IF A.Man[i] # B.Man[i] THEN
  347.           Done := TRUE;
  348.         ELSE
  349.           INC(i);
  350.         END;
  351.       END;
  352.       IF i > MaxQuads THEN
  353.         (* end reached and all digits match *)
  354.         RETURN ExEqual;
  355.       ELSE
  356.         (* compare different digits *)
  357.         IF A.Man[i] < B.Man[i] THEN
  358.           IF A.Sign = positive THEN
  359.             RETURN ExLess;
  360.           ELSE
  361.             RETURN ExGreater;
  362.           END;
  363.         ELSE
  364.           IF A.Sign = positive THEN
  365.             RETURN ExGreater;
  366.           ELSE
  367.             RETURN ExLess;
  368.           END;
  369.         END;
  370.       END;
  371.     END;
  372.   END;
  373. END ExCompare;
  374.  
  375.  
  376. PROCEDURE ExMin *(VAR A : ExNumType; B, C : ExNumType);
  377. (* Return the smaller of B and C in A *)
  378. BEGIN
  379.   IF ExCompare(B, C) = ExGreater THEN
  380.     A := C;
  381.   ELSE
  382.     A := B;
  383.   END;
  384. END ExMin;
  385.  
  386.  
  387. PROCEDURE ExMax *(VAR A : ExNumType; B, C : ExNumType);
  388. (* Return the larger of B and C in A *)
  389. BEGIN
  390.   IF ExCompare(B, C) = ExLess THEN
  391.     A := C;
  392.   ELSE
  393.     A := B;
  394.   END;
  395. END ExMax;
  396.  
  397.  
  398. PROCEDURE ExAddUtility(VAR A : ExNumType; B, C : ExNumType);
  399. (* A := ABS(B) + ABS(C) *)
  400. VAR
  401.   i, j, joff, carry, quad, total : INTEGER;
  402.   Exl1, Ex2 : ExNumType;
  403. BEGIN
  404.   IF IsZero(B) THEN
  405.     A := C;
  406.   ELSIF IsZero(C) THEN
  407.     A := B;
  408.   ELSE
  409.     IF B.Exp > C.Exp THEN
  410.       Exl1 := B;
  411.       Ex2 := C;
  412.     ELSE
  413.       Exl1 := C;
  414.       Ex2 := B;
  415.     END;
  416.     A := Ex0;
  417.     A.Exp := Exl1.Exp;
  418.     carry := 0;
  419.  
  420.     (* shift smallest number until quad-aligned relative to
  421.        larger number *)
  422.     j := (Exl1.Exp - Ex2.Exp) MOD 4;
  423.     FOR i := j TO 1 BY -1 DO
  424.       ExShiftRight(0, Ex2);
  425.       INC(Ex2.Exp);
  426.     END;
  427.     joff := (Ex2.Exp - Exl1.Exp) DIV 4;
  428.  
  429.     (* add the two numbers together *)
  430.     FOR i := MaxQuads TO 0 BY -1 DO
  431.       (* j = index to Ex2 *)
  432.       j := i + joff;
  433.  
  434.       (* check that j falls within array bounds *)
  435.       IF (j >= 0) AND (j <= MaxQuads) THEN
  436.         (* get quad digit from Ex2 *)
  437.         quad := Ex2.Man[j];
  438.       ELSE
  439.         (* j is outside array bounds, use 0 for quad digit *)
  440.         quad := 0;
  441.       END;
  442.  
  443.       (* perform addition with carry *)
  444.       total := Exl1.Man[i] + quad + carry;
  445.  
  446.       (* check for carry *)
  447.       IF total >= 10000 THEN
  448.         DEC(total, 10000);
  449.         carry := 1;
  450.       ELSE
  451.         carry := 0;
  452.       END;
  453.       A.Man[i] := total;
  454.     END;
  455.  
  456.     (* handle final carry *)
  457.     IF carry = 1 THEN
  458.       (* shift carry into top of mantissa *)
  459.       ExShiftRight(carry, A);
  460.  
  461.       (* multiply by ten to update exponent *)
  462.       ExTimes10(A);
  463.     END;
  464.   END;
  465.  
  466.   (* set ExStatus *)
  467.   IF A.Exp > MaxExp THEN
  468.     ExStatus := Overflow;
  469.   END;
  470. END ExAddUtility;
  471.  
  472.  
  473. PROCEDURE ExSubUtility(VAR A : ExNumType; B, C : ExNumType);
  474. (* A := ABS(B) - ABS(C) *)
  475. VAR
  476.   PositiveResult : BOOLEAN;
  477.   i, j, joff, borrow, quad, result : INTEGER;
  478.   Exl1, Ex2 : ExNumType;
  479. BEGIN
  480.   ExAbs(B);
  481.   ExAbs(C);
  482.   IF IsZero(B) THEN
  483.     A := C;
  484.   ELSIF IsZero(C) THEN
  485.     A := B;
  486.   ELSE
  487.     IF B.Exp > C.Exp THEN
  488.       Exl1 := B;
  489.       Ex2 := C;
  490.     ELSE
  491.       Exl1 := C;
  492.       Ex2 := B;
  493.     END;
  494.     PositiveResult := ExCompare(Exl1, Ex2) = ExGreater;
  495.     A := Ex0;
  496.     A.Exp := Exl1.Exp;
  497.     borrow := 0;
  498.  
  499.     (* shift smallest number until quad-aligned relative to
  500.        larger number *)
  501.     j := (Exl1.Exp - Ex2.Exp) MOD 4;
  502.     FOR i := j TO 1 BY -1 DO
  503.       ExShiftRight(0, Ex2);
  504.       INC(Ex2.Exp);
  505.     END;
  506.     joff := (Ex2.Exp - Exl1.Exp) DIV 4;
  507.  
  508.     (* subtract the two numbers *)
  509.     FOR i := MaxQuads TO 0 BY -1 DO
  510.       (* j = index to Ex2 *)
  511.       j := i + joff;
  512.  
  513.       (* check that j falls within array bounds *)
  514.       IF (j >= 0) AND (j <= MaxQuads) THEN
  515.         (* get quad from Ex2 *)
  516.         quad := Ex2.Man[j];
  517.       ELSE
  518.         (* j is outside array bounds, use 0 for quad *)
  519.         quad := 0;
  520.       END;
  521.  
  522.       (* perform subtraction with borrow *)
  523.       IF PositiveResult THEN
  524.         result := Exl1.Man[i] - quad - borrow;
  525.       ELSE
  526.         result := quad - Exl1.Man[i] - borrow;
  527.       END;
  528.  
  529.       (* check for borrow *)
  530.       IF result < 0 THEN
  531.         INC(result, 10000);
  532.         borrow := 1;
  533.       ELSE
  534.         borrow := 0;
  535.       END;
  536.       A.Man[i] := result;
  537.     END;
  538.   END;
  539.  
  540.   (* normalise *)
  541.   ExNorm(A);
  542.  
  543.   (* adjust sign *)
  544.   IF ExCompare(B, C) = ExLess THEN
  545.     ExChgSign(A);
  546.   END;
  547. END ExSubUtility;
  548.  
  549.  
  550. PROCEDURE ExAdd *(VAR A : ExNumType; B, C : ExNumType);
  551. (* A = B + C *)
  552. BEGIN
  553.   IF B.Sign = C.Sign THEN
  554.     (* B and C have the same sign -- just add *)
  555.     ExAddUtility(A, B, C);
  556.     IF B.Sign = negative THEN
  557.       ExChgSign(A);
  558.     END;
  559.   ELSE
  560.     (* B and C have different signs *)
  561.     IF B.Sign = positive THEN
  562.       ExSubUtility(A, B, C);
  563.     ELSE
  564.       ExSubUtility(A, C, B);
  565.     END;
  566.   END;
  567. END ExAdd;
  568.  
  569.  
  570. PROCEDURE ExSub *(VAR A : ExNumType; B, C : ExNumType);
  571. (* A = B - C *)
  572. BEGIN
  573.   ExChgSign(C);   (* A = B + (-C) *)
  574.   ExAdd(A, B, C);
  575. END ExSub;
  576.  
  577.  
  578. PROCEDURE ExRound *(VAR A : ExNumType; D : INTEGER);
  579. (* A := Round(A) *)
  580. VAR
  581.   cindex, index, digit, i : INTEGER;
  582.   Exl : ExNumType;
  583. BEGIN
  584.   IF D <= MaxDigits-1 THEN
  585.     index := (D+1) DIV 4;
  586.     digit := A.Man[index];
  587.     cindex := (D + 1) MOD 4;
  588.     IF cindex = 0 THEN
  589.       digit := digit DIV 1000;
  590.     ELSIF cindex = 1 THEN
  591.       digit := digit DIV 100;
  592.     ELSIF cindex = 2 THEN
  593.       digit := digit DIV 10;
  594.     END;
  595.     IF digit MOD 10 >= 5 THEN
  596.       (* round up *)
  597.       Exl := Ex1;
  598.       Exl.Exp := A.Exp - D;
  599.       IF A.Sign = negative THEN
  600.         ExChgSign(Exl);
  601.       END;
  602.       ExAdd(A, A, Exl);
  603.     END;
  604.  
  605.     (* make remaining digits zero *)
  606.     IF cindex = 0 THEN
  607.       A.Man[index] := 0;
  608.     ELSIF cindex = 1 THEN
  609.       A.Man[index] := A.Man[index] DIV 1000 * 1000;
  610.     ELSIF cindex = 2 THEN
  611.       A.Man[index] := A.Man[index] DIV 100 * 100;
  612.     ELSIF cindex = 3 THEN
  613.       A.Man[index] := A.Man[index] DIV 10 * 10;
  614.     END;
  615.     FOR i := index+1 TO MaxQuads DO
  616.       A.Man[i] := 0;
  617.     END;
  618.   END;
  619. END ExRound;
  620.  
  621.  
  622. PROCEDURE ExMult *(VAR A : ExNumType; B, C : ExNumType);
  623. (* Return B * C *)
  624. VAR
  625.   i, j, carry : INTEGER;
  626.   product : LONGINT;
  627.   Exl : ExNumType;
  628. BEGIN
  629.   IF (ExCompare(B,Ex0) = ExEqual) OR (ExCompare(C,Ex0) = ExEqual) THEN
  630.     (* multiplication by zero *)
  631.     A := Ex0;
  632.   ELSIF ExCompare(C,Ex1) = ExEqual THEN
  633.     A := B;
  634.   ELSIF ExCompare(B,Ex1) = ExEqual THEN
  635.     A := C;
  636.   ELSE
  637.     (* real multiplication *)
  638.     A := Ex0;
  639.     FOR i := MaxQuads TO 0 BY -1 DO
  640.       Exl := Ex0;
  641.       Exl.Exp := B.Exp + C.Exp - i * 4 - 3;
  642.       carry := 0;
  643.       FOR j := MaxQuads TO 0 BY -1 DO
  644.         product := LONG(B.Man[j]) * LONG(C.Man[i]) + LONG(carry);
  645.         Exl.Man[j] := SHORT(product MOD 10000);
  646.         carry := SHORT(product DIV 10000);
  647.       END;
  648.  
  649.       (* check for final carry *)
  650.       WHILE carry > 0 DO
  651.         ExShiftRight(carry MOD 10, Exl);
  652.         ExTimes10(Exl);
  653.         carry := carry DIV 10;
  654.       END;
  655.  
  656.       (* perform summation *)
  657.       ExAddUtility(A, A, Exl);
  658.     END;
  659.  
  660.     (* adjust product sign *)
  661.     IF B.Sign # C.Sign THEN
  662.       ExChgSign(A);
  663.     END;
  664.   END;
  665. END ExMult;
  666.  
  667.  
  668. PROCEDURE ExDiv *(VAR A : ExNumType; B, C : ExNumType);
  669. (* A := B / C *)
  670. VAR
  671.   i, j : INTEGER;
  672.   quotient : LONGINT;
  673.   Exl1, Ex2 : ExNumType;
  674. BEGIN
  675.   IF IsZero(C) THEN
  676.     (* attempt to divide by zero *)
  677.     ExStatus := DivideByZero;
  678.   ELSIF IsZero(B) THEN
  679.     (* dividend = 0 *)
  680.     A := Ex0;
  681.   ELSIF ExCompare(C,Ex1) = ExEqual THEN
  682.     (* divisor = 1 *)
  683.     A := B;
  684.   ELSE
  685.     (* real division *)
  686.     A := Ex0;
  687.     A.Exp := B.Exp - C.Exp;
  688.  
  689.     (* adjust quotient sign *)
  690.     IF B.Sign # C.Sign THEN
  691.       ExChgSign(A);
  692.     END;
  693.  
  694.     (* let Exl1 = ABS(B) / magnitude of B *)
  695.     Exl1 := B;
  696.     ExAbs(Exl1);
  697.     Exl1.Exp := 0;
  698.  
  699.     (* let Ex2 = ABS(C) / magnitude of C *)
  700.     Ex2 := C;
  701.     ExAbs(Ex2);
  702.     Ex2.Exp := 0;
  703.  
  704.     (* actual division *)
  705.     FOR i := 0 TO MaxDigits-1 DO
  706.       quotient := 0;
  707.       WHILE ExCompare(Exl1, Ex2) >= ExEqual DO
  708.         INC(quotient);
  709.         ExSubUtility(Exl1, Exl1, Ex2);
  710.       END;
  711.       A.Man[i DIV 4] := A.Man[i DIV 4] * 10 + SHORT(quotient);
  712.       ExDiv10(Ex2);
  713.     END;
  714.  
  715.     (* normalize quotient *)
  716.     ExNorm(A);
  717.   END;
  718. END ExDiv;
  719.  
  720.  
  721. (* $CopyArrays- *)
  722. PROCEDURE StrToExNum *(Str : ARRAY OF CHAR; VAR A : ExNumType);
  723. (* Convert the string `Str' into an extended real number in A. *)
  724. VAR
  725.   Exp, NumbIndex, InCnt, EndCnt : INTEGER;
  726.   ZeroFlag, NegativeExponent, LeftSide, InExponent : BOOLEAN;
  727.   Done, NegExponent : BOOLEAN;
  728.   ActiveChar : CHAR;
  729.  
  730.   PROCEDURE SetDigit(VAR Numb : INTEGER);
  731.   BEGIN
  732.     Numb := Numb * 10 + ORD(Str[InCnt]) - ORD('0');
  733.   END SetDigit;
  734.  
  735. BEGIN
  736.   (* initialize a few counters and stuff *)
  737.   A := Ex0;
  738.   InCnt := 0;             (* character counter *)
  739.   Exp := 0;               (* working exponent *)
  740.   LeftSide := TRUE;
  741.   InExponent := FALSE;
  742.   ZeroFlag := TRUE;
  743.   NegativeExponent := FALSE;
  744.   EndCnt := SHORT(S.Length(Str));
  745.   NumbIndex := 0;
  746.  
  747.   (* set the sign of `A' to a negative -- if needed *)
  748.   WHILE (InCnt < EndCnt) & (Str[InCnt] = ' ') DO INC(InCnt) END;
  749.   IF Str[InCnt] = '-' THEN
  750.     A.Sign := negative;
  751.     INC(InCnt);
  752.   END;
  753.   WHILE InCnt < EndCnt DO
  754.     ActiveChar := Str[InCnt];
  755.     IF (ActiveChar >= '0') & (ActiveChar <= '9') THEN
  756.       IF InExponent THEN
  757.         SetDigit(Exp);
  758.       ELSE
  759.         IF NumbIndex < MaxDigits THEN  (* enter a digit *)
  760.           SetDigit(A.Man[NumbIndex DIV 4]);
  761.         END;
  762.         IF ZeroFlag & (Str[InCnt] # '0') THEN
  763.           ZeroFlag := FALSE;
  764.         END;
  765.         IF NOT ZeroFlag THEN
  766.           INC(NumbIndex);
  767.           IF LeftSide THEN INC(A.Exp) END;
  768.         ELSE
  769.           IF NOT LeftSide & (A.Exp <= 0) THEN DEC(A.Exp) END;
  770.         END;
  771.       END;
  772.     ELSIF ActiveChar = '.' THEN
  773.       IF ~LeftSide THEN ExStatus := IllegalNumber END;
  774.       LeftSide := FALSE;
  775.     ELSIF ActiveChar = 'E' THEN
  776.       InExponent := TRUE;
  777.       IF Str[InCnt+1] = '-' THEN
  778.         NegativeExponent := TRUE;
  779.         INC(InCnt);
  780.       ELSIF Str[InCnt+1] = '+' THEN
  781.         INC(InCnt);
  782.       END;
  783.     ELSIF ActiveChar = ' ' THEN
  784.       (* do nothing if blanks are encountered *)
  785.     ELSE
  786.       ExStatus := IllegalNumber;
  787.     END; (* IF *)
  788.     INC(InCnt);
  789.   END;
  790.  
  791.   (* fix up the last quad digits *)
  792.   WHILE (NumbIndex DIV 4 <= MaxQuads) & (NumbIndex MOD 4 > 0) DO
  793.     A.Man[NumbIndex DIV 4] := A.Man[NumbIndex DIV 4] * 10;
  794.     INC(NumbIndex);
  795.   END;
  796.  
  797.   (* Do some final fixes to the exponent *)
  798.   IF NegativeExponent THEN
  799.     DEC(A.Exp, Exp);
  800.   ELSE
  801.     INC(A.Exp, Exp);
  802.   END;
  803.   DEC(A.Exp);
  804.  
  805.   (* Ensure valid zero value *)
  806.   IF IsZero(A) THEN A := Ex0 END;
  807. END StrToExNum;
  808.  
  809.  
  810. PROCEDURE GetDigit(VAR ExpStr : ARRAY OF CHAR; VAR StrCnt : INTEGER;
  811.                    A : ExNumType; VAR ManIndex : INTEGER) : CHAR;
  812. VAR Quad : LONGINT;
  813.     Ok : BOOLEAN;
  814. BEGIN
  815.   (* Passing all parameters due to a bug in Oberon-2 when this
  816.      was a local procedure *)
  817.   INC(StrCnt);
  818.   IF StrCnt = 4 THEN (* get a quad of digits *)
  819.     Quad := A.Man[ManIndex];
  820.     Ok := Cnv.IntToStr(Quad,ExpStr,Dec,5,'0');
  821.     S.Delete(ExpStr, 0, 1);   (* remove leading digit *)
  822.     INC(ManIndex);
  823.     StrCnt := 0;
  824.   END;
  825.   RETURN ExpStr[StrCnt];
  826. END GetDigit;
  827.  
  828.  
  829. PROCEDURE ExNumToStr *(A : ExNumType; Decimal, ExpWidth : INTEGER;
  830.                        VAR Str : ARRAY OF CHAR);
  831. (* Convert the extended real number into a string `S'. *)
  832. VAR
  833.   pos, ManIndex, StrCnt, InCnt, Aexp, MaxExpWidth : INTEGER;
  834.   ExpStr : ARRAY 41 OF CHAR;
  835.   FixPoint, Ok : BOOLEAN;
  836.  
  837.   PROCEDURE ConcatChar(ch : CHAR);
  838.   BEGIN
  839.     Str[pos] := ch;
  840.     INC(pos);
  841.   END ConcatChar;
  842.  
  843. BEGIN
  844.   (* initialize a few parameters *)
  845.   pos := 0;
  846.   StrCnt := 3;
  847.   ManIndex := 0;
  848.   ExpStr := '';
  849.  
  850.   (* force scientific notation for numbers too small or too large *)
  851.   Aexp := ABS(A.Exp);
  852.   MaxExpWidth := ExpWidth;
  853.   IF ((ExpWidth = 0) AND (Aexp > MaxDigits)) OR (ExpWidth > 0) THEN
  854.     (* force scientific notation *)
  855.     IF Aexp > 9999 THEN ExpWidth := 5
  856.     ELSIF Aexp > 999 THEN ExpWidth := 4
  857.     ELSIF Aexp > 99 THEN ExpWidth := 3
  858.     ELSIF Aexp > 9 THEN ExpWidth := 2
  859.     ELSE ExpWidth := 1
  860.     END;
  861.   END;
  862.   IF MaxExpWidth < ExpWidth THEN MaxExpWidth := ExpWidth END;
  863.  
  864.   (* add the negative sign to the number *)
  865.   IF A.Sign = negative THEN ConcatChar('-') END;
  866.  
  867.   (* ensure we don't exceed the maximum digits *)
  868.   FixPoint := Decimal # 0;
  869.   IF (Decimal > MaxDigits) OR NOT FixPoint THEN
  870.     Decimal := MaxDigits-1;
  871.   END;
  872.  
  873.   (* convert the number into scientific notation *)
  874.   IF MaxExpWidth > 0 THEN
  875.     ExRound(A, Decimal);    (* round to appropriate decimal places *)
  876.     ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex)); (* leading digit *)
  877.     ConcatChar('.');        (* decimal point *)
  878.     FOR InCnt := 1 TO Decimal DO
  879.       ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex));     (* add following digits *)
  880.     END;
  881.  
  882.     (* add the exponent *)
  883.     ConcatChar('E');
  884.     IF A.Exp >= 0 THEN ConcatChar('+') ELSE ConcatChar('-') END;
  885.     ConcatChar(0X);                        (* terminate the string *)
  886.  
  887.     Ok := Cnv.IntToStr(Aexp,ExpStr,Dec,SHORT(MaxExpWidth),'0');
  888.     S.Append(Str, ExpStr);
  889.   ELSE
  890.     (* format a non-scientific number *)
  891.     ExRound(A, Decimal+A.Exp); (* round to decimal places *)
  892.     IF A.Exp < 0 THEN
  893.       ConcatChar('0');         (* leading digit *)
  894.       ConcatChar('.');         (* decimal point *)
  895.       FOR InCnt := 2 TO ABS(A.Exp) DO   (* pad with leading zeros *)
  896.         ConcatChar('0');
  897.       END;
  898.       INC(Decimal, A.Exp+1);
  899.     END;
  900.     InCnt := 0;
  901.     REPEAT
  902.       ConcatChar(GetDigit(ExpStr, StrCnt, A, ManIndex));
  903.       IF InCnt > A.Exp THEN
  904.         DEC(Decimal);
  905.       ELSIF InCnt = A.Exp THEN
  906.         ConcatChar('.');
  907.       END;
  908.       INC(InCnt);
  909.     UNTIL (InCnt = MaxDigits) OR (Decimal = 0);
  910.     ConcatChar(0X);
  911.  
  912.     (* remove any trailing zeros and unneeded digits *)
  913.     InCnt := pos - 2;
  914.     WHILE (InCnt > 1) & (Str[InCnt] = '0') & NOT FixPoint DO
  915.       Str[InCnt] := 0X;
  916.       DEC(InCnt);
  917.     END;
  918.   END;
  919. END ExNumToStr;
  920.  
  921.  
  922. PROCEDURE WriteExNum *(A : ExNumType;
  923.                        Width, Decimal, ExpWidth : INTEGER);
  924. (* Write out A to the current output stream in a field of
  925.    `Width' characters, with `Decimal' decimal places, and
  926.    `ExpWidth' exponent width. *)
  927. VAR
  928.   Str : ARRAY MaxLengthNumber+1 OF CHAR;
  929.   i, len : INTEGER;
  930. BEGIN
  931.   ExNumToStr(A, Decimal, ExpWidth, Str);
  932.   len := SHORT(S.Length(Str));
  933.   IF Width >= len THEN
  934.     FOR i := 1 TO Width-len DO io.Write(" ") END;
  935.   END;
  936.   io.WriteString(Str);
  937. END WriteExNum;
  938.  
  939.  
  940. PROCEDURE ExNumb *(LeftMan : LONGINT; RightMan : LONGINT;
  941.                    ExpShift : INTEGER; VAR A : ExNumType);
  942. (* create an extended real number which has LeftMan to the left
  943.    of the decimal point and RightMan to the right. The ExpShift
  944.    quantity can shift the decimal point to the right for negative
  945.    values; to the left for positive values. *)
  946. VAR
  947.   i : INTEGER;
  948. BEGIN
  949.   A := Ex0;
  950.   IF LeftMan < 0 THEN
  951.     A.Sign := negative;
  952.     LeftMan := -LeftMan;
  953.   END;
  954.   WHILE RightMan # 0 DO
  955.     ExShiftRight(SHORT(RightMan MOD 10), A);(* shift right 1 position *)
  956.     RightMan := RightMan DIV 10;
  957.   END;
  958.   WHILE LeftMan # 0 DO
  959.     ExShiftRight(SHORT(LeftMan MOD 10), A); (* shift right 1 position *)
  960.     ExTimes10(A);                    (* adjust the exponent *)
  961.     LeftMan := LeftMan DIV 10;
  962.   END;
  963.   ExDiv10(A);                        (* final exponent adjust *)
  964.   INC(A.Exp, ExpShift);              (* shift the decimal point *)
  965.   IF A.Exp > MaxExp THEN             (* signal any errors *)
  966.     ExStatus := Overflow;
  967.   ELSIF A.Exp < MinExp THEN
  968.     ExStatus := Underflow;
  969.   END;
  970. END ExNumb;
  971.  
  972.  
  973. BEGIN
  974.   (* create extended number 0 *)
  975.   Ex0.Sign := positive;
  976.   FOR MaxDigits := 0 TO LEN(Ex0.Man)-1 DO
  977.     Ex0.Man[MaxDigits] := 0;
  978.   END;
  979.   Ex0.Exp := 0;
  980.  
  981.   (* default to max number of digits *)
  982.   SetMaxDigits(HighBoundsManArray);
  983.  
  984.   (* create some extended number constants *)
  985.   ExNumb(1, 0, 0, Ex1);     (* 1.0 *)
  986.  
  987.   StrToExNum(
  988.   "3.14159265358979323846264338327950288419716939937511", pi);
  989.   StrToExNum(
  990.   "2.71828182845904523536028747135266249775724709369996", e);
  991.   StrToExNum(
  992.   "0.69314718055994530941723212145817656807550013436026", ln2);
  993.   StrToExNum(
  994.   "2.30258509299404568401799145468436420760110148862877", ln10);
  995. END ExNumbers.
  996.